package br.edu.utfpr.bsi.si.team.jogadores;

import java.rmi.RemoteException;

import robocode.rescue.RobotInfo;
import robocode.rescue.interfaces.RMIRobotInterface;
import br.edu.utfpr.bsi.si.Config;
import br.edu.utfpr.bsi.si.team.Ambiente;

public class Robo {

	public static Ambiente ambiente;
	
	// atributos em comum	
	public RobotInfo robo;
	public int num;
	
	// atributos dos nossos robos
	public RMIRobotInterface rinterface;	
	public boolean voltando = false;
	public char funcao = 'A';	// A:Atacante / D:Defensor
	public boolean superJogador = false;
	
	// atributos dos adversarios
	public Robo estaSendoDefendidoPor;

	public void move() throws RemoteException {
		System.out.println("Pos" + this.robo.getName() + " X:Y " + this.robo.getX() + ":" + this.robo.getY() + " SPD:" + this.robo.getVelocity() + " SEGUE:" + ambiente.refem.rinterface.isFollowing());

		if (this.funcao == 'A') {
			this.ataca();
		} else {
			this.defende();
		}
	}

	private void ataca() throws RemoteException {
		if ( ambiente.refem.rinterface.isFollowing() == 0) {
			voltando = false;
		}
		
		if ( ambiente.refem.rinterface.isFollowing() == num) {
			// virar 180 graus se pegou o prisioneiro agora
			if ( !voltando ) {
				this.rinterface.setTurnLeft(180);
				this.rinterface.setAhead(100);
				this.rinterface.execute();
			}
			
			double distanciaAdversarioMaisProximo = this.distanciaAdversarioMaisProximo();
			double distanciaAmigoMaisProximo = this.distanciaAmigoMaisProximo();

			System.out.println("	DistProxEnemy = " + distanciaAdversarioMaisProximo);
			System.out.println("	DistProxFriend = " + distanciaAmigoMaisProximo);
			
			if (this.robo.getVelocity() <= 0.0 && (distanciaAdversarioMaisProximo <= 50.0 || distanciaAmigoMaisProximo <= 50.0)) {
				this.rinterface.setTurnRight(90);
				this.rinterface.execute();
				this.rinterface.ahead(50);
				this.rinterface.execute();
			}
			
			double [] destino = new double[2];
			
			destino[0] = (Config.Lado == 1) ? 0 : Config.largura;
			
			if (!voltando)
				destino[1] = (voltaPorCima()) ? Config.altura : 0;
			
			for (int inimigo = 1; inimigo < Config.numRobos; inimigo++) {
				if (verificaReta(ambiente.robosInimigos[inimigo], destino)) {
					goTo(this.robo.getX() + (100 * Config.Lado), this.robo.getY() - (50 * Config.Lado));
					break;
				} else {
					goTo(destino[0], destino[1]);
				}
			}
			voltando = true;
			
		} else if ( ambiente.refem.rinterface.isFollowing() != 0 && !voltando ) { 
			this.rinterface.setTurnLeft(90);
			this.rinterface.setAhead(100);
			this.rinterface.execute();
			voltando = true;
			
		} else {
//			voltando = false;
			double distanciaAdversarioMaisProximo = this.distanciaAdversarioMaisProximo();
			double distanciaAmigoMaisProximo = this.distanciaAmigoMaisProximo();

			System.out.println("	DistProxEnemy = " + distanciaAdversarioMaisProximo);
			System.out.println("	DistProxFriend = " + distanciaAmigoMaisProximo);
			
			if (this.robo.getVelocity() <= 0.0 && distanciaAdversarioMaisProximo <= 50.0 && distanciaAmigoMaisProximo <= 50.0) {
				this.rinterface.setTurnRight(90);
				this.rinterface.execute();
				this.rinterface.ahead(50);
				this.rinterface.execute();
				return;
			} else {
				if (this.superJogador) {
		
				} else {
					double diff = (this.robo.getX() - (Config._1_2 - 200)) * Config.Lado;
					if (diff >= 0) { // ta no ataque
						for (int inimigo = 1; inimigo < Config.numRobos; inimigo++) {
							if (verificaReta(ambiente.robosInimigos[inimigo], new double[] { ambiente.refem.robo.getX(), ambiente.refem.robo.getY() })) {
								if (this.robo.getX() == ambiente.refem.robo.getX()) {
									goTo(this.robo.getX() - (100 * Config.Lado), this.robo.getY() + (100 * Config.Lado));
								} else {
									goTo(this.robo.getX() + (100 * Config.Lado), this.robo.getY() - (50 * ((Math.random()<0.5)?-1:1)));
								}
								break;
							} else {
								goTo(ambiente.refem.robo.getX(), ambiente.refem.robo.getY());
							}
						}
					} else {
						this.rinterface.setAhead(Math.abs(diff));
						this.rinterface.execute();
					}
				}
			}
		}
	}

	private boolean voltaPorCima(){
		int numCima = 0;
		for (Robo inimigo : ambiente.robosInimigos) {
			if (inimigo.robo.getY() > (Config.altura/2)) {
				numCima++;
			}
		}
		return (numCima <= 2);
	}

	private void defende() throws RemoteException {
		if (this.superJogador) {

		} else {
			Robo adversarioDefende = this.adversarioDefende();
			
			//estou entre o refem e o adversario
			if (this.distanciaPrisioneiro() <= adversarioDefende.distanciaPrisioneiro()) {
//				double x = this.robo.getX(), y = this.robo.getY();
				double x1 = ambiente.prisioneiro.robo.getX(), y1 = ambiente.prisioneiro.robo.getY();
				double x2 = adversarioDefende.robo.getX(), y2 = adversarioDefende.robo.getY();

				double a = (y1 - y2), b = (x2 - x1), c = (x1 * y2 - x2 * y1);
				
//				double distanciaAteMeio = (Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b));
//				double distanciaAtePonto = Math.sqrt(Math.pow(this.distancia(adversarioDefende), 2) - Math.pow(distanciaAteMeio, 2));
				
				double gotoX, gotoY;
//				a * x + b * y + c = 0
//				-b*y) = a*x + c
//				if (distanciaAteMeio > distanciaAtePonto) {
				if ( Math.abs(x1 - x2) > Math.abs(y1 - y2) ) {
					gotoX = (ambiente.prisioneiro.robo.getX() + 200);
					gotoY = (a * gotoX + c) / b * -1;
					System.out.println("GotoX " + gotoX + ":" + gotoY);
				} else {
					gotoY = (ambiente.prisioneiro.robo.getY() + 200);
					gotoX = (b * gotoY + c) / a * -1;
					System.out.println("GotoY " + gotoX + ":" + gotoY);
				}
//				} else {
					
//				}
					
				this.goTo(gotoX, gotoY, true);
			}
			//o adversario esta entre o refem e eu
			else {
				this.goTo(adversarioDefende.robo.getX(), adversarioDefende.robo.getY());
			}
			//o refem esta entre o adversario e eu
//			else {
//				
//			}
			
		}
	}

	public static Ambiente getAmbiente() {
		return ambiente;
	}

	public static void setAmbiente(Ambiente time) {
		Robo.ambiente = time;
	}

	public void goTo(double x, double y) throws RemoteException {

		goTo(x, y, false);

	}

	public void goTo(double x, double y, boolean viraAntes)
			throws RemoteException {

		double alfa;
		double absX = Math.abs(x - this.robo.getX()), absY = Math.abs(y
				- this.robo.getY());
		double beta = Math.toDegrees(Math.atan(absY / absX));
		double distancia = Math.sqrt(absX * absX + absY * absY);

		if (x < this.robo.getX()) {
			// indo para a esquerda

			alfa = 270 - this.robo.getHeading();

			if (y > this.robo.getY()) {
				alfa += beta;
			} else {
				alfa -= beta;
			}

			if (Math.abs(alfa) > 180) {
				alfa -= 360;
			}

		} else {
			// indo para a direita

			alfa = 90 - this.robo.getHeading();

			if (y > this.robo.getY()) {
				alfa -= beta;
			} else {
				alfa += beta;
			}

			if (Math.abs(alfa) > 180) {
				alfa = 360 - alfa;
			}
		}

		// if ((robo.getHeading() > alfa + 5) || (robo.getHeading() < alfa - 5))
		// {
		this.rinterface.setTurnRight(alfa);
		
		if ( viraAntes )
			this.rinterface.execute();
		
		this.rinterface.setAhead(distancia);
		this.rinterface.execute();

//		System.out.println("Alfa : " + alfa);
//		System.out.println("Beta : " + beta);
//		System.out
//				.println("X,Y : " + this.robo.getX() + "," + this.robo.getY());
//		System.out.println("Xdest,Ydest : " + x + "," + y);

	}

	/**
	 * 
	 * Verifica se a distancia do roboB ateh a reta que liga o roboA com o ponto
	 * C eh menor que 50
	 * 
	 * @param roboB
	 * @param refem
	 * @return true caso seja menor ou igual, false caso seja maior
	 */
	public boolean verificaReta(Robo roboB, double[] ponto) {
		double x = roboB.robo.getX(), y = roboB.robo.getY(), x1 = this.robo.getX(), y1 = this.robo
				.getY(), x2 = ponto[0], y2 = ponto[1];

		double a = (y1 - y2), b = (x2 - x1), c = (x1 * y2 - x2 * y1);

		return ((Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b)) <= 50);
	}
	
	public double distancia(double x, double y) {
		return Math.sqrt(Math.pow(x - this.robo.getX(), 2) + Math.pow(y - this.robo.getY(), 2));
	}
	
	public double distancia(Robo outroRobo) {
		return this.distancia(outroRobo.robo.getX(), outroRobo.robo.getY());
	}
	
	public double distanciaRefem(){
		return this.distancia(ambiente.refem);
	}
	
	public double distanciaPrisioneiro(){
		return this.distancia(ambiente.prisioneiro);
	}
	
	public double distanciaAdversarioMaisProximo(){
		double distMin = 9999; 
		double dist;
		for (Robo roboAdversario : ambiente.robosInimigos) {
			dist = this.distancia(roboAdversario);
			if ( dist < distMin )
				distMin = dist;
		}
		
		return distMin;
	}
	
	public double distanciaAmigoMaisProximo(){
		double distMin = 9999; 
		double dist;
		for (Robo roboAmigo : ambiente.robos) {
			dist = this.distancia(roboAmigo);
			if ( dist < distMin )
				distMin = dist;
		}
		
		return distMin;
	}
	
	public Robo adversarioDefende(){
		double distMin = 9999; 
		double dist;
		Robo adversario = null;
		for (Robo roboAdversario : ambiente.robosInimigos) {
			if (roboAdversario.estaSendoDefendidoPor != null && roboAdversario.estaSendoDefendidoPor == this) {
				return roboAdversario;
			}
			
			dist = this.distancia(roboAdversario);
			if ( dist < distMin ) {
				distMin = dist;
				adversario = roboAdversario;
			}
		}
		
		return adversario;
	}

}
